package com.scope.imageFilters;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;

import org.opencv.android.Utils;
import org.opencv.core.Mat;

import java.io.File;
import java.io.FileOutputStream;

/**
 * Class: BaseFilter
 * Author: Samrith
 *
 * Description:
 * A general image processing operator is a function that takes one or more input images
 * and produces an output image.
 * In this kind of image processing transform, each output pixels value depends on only
 * the corresponding input pixel value (plus, potentially, some globally collected information
 * or parameters).
 *
 * This class is the base of all image filters class.
 * The flow of this class is constructor() -> setParam() -> process()
 *
 * It is required to set a name to mName to differentiate child class by mName = getFilterName();
 * It is recommended to override setParam(Object param) for respective child param constraint
 * It is required to override applyFilter() for the filtering process of respective child
 */

public class BaseFilter<ParamType> {
    protected BaseFilter(Context context) {
        mContext = context;
    }

    public void setSourcePath(String sourcePath) {
        mSourcePath = sourcePath;
        mDestPath = SCOPE_PATH + "/" + mName + "/" + (new File(sourcePath)).getName();

        mParam = null;
        mSourceImage = null;
        mSourceImageMat = null;
        mFilteredImageMat = null;
    }

    public void setSourceImage(Bitmap image) {
        mSourcePath = null;
        mDestPath = null;

        mParam = null;
        mSourceImage = image;
        mSourceImageMat = null;
        mFilteredImageMat = null;
    }

    public void setSourceImageMat(Mat sourceImage) {
        mSourcePath = null;
        mDestPath = null;

        mParam = null;
        mSourceImage = null;
        mSourceImageMat = sourceImage;
        mFilteredImageMat = null;
    }

    public final static String Tag = "SCOPE 2";

    public void process() {
        long timeStamp;
        timeStamp = System.currentTimeMillis();
        readImage();
        Log.v(Tag, mName + ": read image: " + (System.currentTimeMillis() - timeStamp));
        timeStamp = System.currentTimeMillis();
        applyFilter();
        Log.v(Tag, mName + ": filter image: " + (System.currentTimeMillis() - timeStamp));
        timeStamp = System.currentTimeMillis();
        writeImage();
        Log.v(Tag, mName + ": write image: " + (System.currentTimeMillis() - timeStamp));
    }

    protected Context mContext;
    public Context getContext() {
        return mContext;
    }

    protected String mSourcePath;
    public String getSourcePath() {
        return mSourcePath;
    }

    protected String mDestPath;
    public String getDestPath() {
        return mDestPath;
    }

    private Bitmap mSourceImage;
    public Bitmap getSourceImage() {
        return mSourceImage;
    }

    protected Mat mSourceImageMat;
    public Mat getSourceImageMat() {
        return mSourceImageMat;
    }

    private void readImage() {
        if (mSourcePath != null) {
            mSourceImage = BitmapFactory.decodeFile(mSourcePath);

            if (mSourceImage != null) {
                Log.v(Tag, mName + ": image retrieved from: " + mSourcePath);
                Log.v(Tag, mName + ": image size: " + mSourceImage.getByteCount());
            } else {
                Log.v(Tag, mName + ": no image found: " + mSourcePath);
            }
        }

        if (mSourceImageMat == null) {
            mSourceImageMat = new Mat();
            Utils.bitmapToMat(mSourceImage, mSourceImageMat);
        }
    }

    private Bitmap mFilteredImage;
    public Bitmap getFilteredImage() {
        return mFilteredImage;
    }

    protected Mat mFilteredImageMat;
    public Mat getFilteredImageMat() {
        return mFilteredImageMat;
    }

    private void writeImage() {
        if (mFilteredImageMat != null) {
        	mFilteredImage = Bitmap.createBitmap(
        			mFilteredImageMat.cols(),
        			mFilteredImageMat.rows(),
        			Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(mFilteredImageMat, mFilteredImage);
        } else {
        	mFilteredImage = mSourceImage;
        }

        if (mDestPath != null) {
            try {
                FileOutputStream out = new FileOutputStream(mDestPath);
                boolean compressible = mFilteredImage.compress(Bitmap.CompressFormat.PNG, 100, out);
                out.close();

                if (compressible) {
                    Log.v(Tag, mName + ": image compressed.");
                } else {
                    Log.v(Tag, mName + ": image not compressible.");
                }

                if (mFilteredImageMat != null) {
                    Log.v(Tag, mName + ": image written to: " + mDestPath);
                } else {
                    Log.v(Tag, mName + ": no image filtered.");
                    Log.v(Tag, mName + ": original image written to: " + mDestPath);
                }

                Log.v(Tag, mName + ": image size: " + mFilteredImage.getByteCount());
            } catch (Exception e) {
                Log.d(Tag, mName + ": file cannot be written: " + mDestPath);
            }
        }
    }

    protected ParamType mParam;
    public void setParam(ParamType param) {
        mParam = param;
    }

    public ParamType getParam() {
        return mParam;
    }

    protected void applyFilter() {
    }

    protected Mat MatZeros() {
        if (mSourceImageMat == null) {
            return null;
        } else {
            return new Mat(mSourceImageMat.size(), mSourceImageMat.type());
        }
    }

    protected String mName;
    public static String getFilterName() {
        return "BaseFilter";
    }

    public static final String SCOPE_PATH = Environment.getExternalStorageDirectory().getPath() + "/Scope 2";
}
